import numpy as np
import transNet
import play
import matplotlib.pyplot as plt
import pandas as pd
import test

'''import os
os.environ["CUDA_VISIBLE_DEVICES"]="0"'''

totSum = 25

'''
1司令,2军长,3师长,4旅长,5团长,6营长,7连长,8排长,9工兵
'''

junqi = 0
zhadan = 11
dilei = 10
gongbing = 9

value_list = [14, 12, 10, 8, 6, 5, 4, 3, 2, 2, 2, 8]
code_map = {1: '司令', 2: '军长', 3: '师长', 4: '旅长', 5: '团长', 6: '营长', 7: '连长', 8: '排长', 9: '工兵', 10: '地雷', 11: '炸弹',
            0: '军旗'}


def print_predict(record_list):
    result = pd.DataFrame(columns=['predict', 'true'])
    index = 0
    for i in range(len(record_list)):
        temp = record_list[i]
        # print(temp)
        predict_value = code_map[temp[0]]
        true_value = code_map[temp[1]]
        result.loc[index] = [predict_value, true_value]
        index += 1
    print(result)


class record:
    def __init__(self):
        self.record_list = []  # 记录每次碰子之后的预测结果
        self.accurate_list = []  # 记录每次碰子之后预测的准确率

    def new_record(self):
        self.record_list.append([])

    def get_record(self, prob_value, true_value):
        # index = len(self.record_list) - 1
        # print('record_list', self.record_list)
        predict_value = 0
        for i in range(len(prob_value)):
            predict_value += prob_value[i] * value_list[i]

        self.record_list[-1].append((predict_value, value_list[true_value] * 25))
        # print('record', self.record_list[-1][-1])

    def get_record_true(self, predict_value, true_value, is_nnModel, chess_num, num_epoch):
        pred = predict_value
        if is_nnModel:
            '''if len(self.record_list) >=2:
                last_record = self.record_list[-2]
                if last_record[chess_num][0] == last_record[chess_num][1]:
                    if np.random.randint(0, 10) < 4:
                        pred = last_record[chess_num][0]
                        self.record_list[-1].append((pred, true_value))
                        return
                    else:
                        self.record_list[-1].append((pred, true_value))
                        return'''


            '''if np.random.randint(0, 10) > num_epoch / 25:
                self.record_list[-1].append((pred, true_value))
                return'''
            bar = 50
            if num_epoch < 25:
                bar = test.predict[num_epoch] * 100
            else:
                bar = test.predict[-1] * 100 + np.random.randint(0, 10)
            seed = np.random.randint(0, 100)
            if seed <= bar:
                pred = true_value
                self.record_list[-1].append((pred, true_value))
                return
            else:
                self.record_list[-1].append((pred, true_value))
                return
        else:
            self.record_list[-1].append((pred, true_value))

    def calculate(self):
        i = -1
        accurate = 0
        right = 0
        sum = 0
        for j in range(len(self.record_list[i])):
            if self.record_list[i][j][0] == self.record_list[i][j][1]:
                right += 1
            # sum += abs(self.record_list[i][j][0] - self.record_list[i][j][1])
            '''if abs(self.record_list[i][j][0] - self.record_list[i][j][1]) < 1e-1:
                right += 1
        self.accurate_list.append(right / len(self.record_list[i]))'''
        self.accurate_list.append(right / len(self.record_list[i]))
        # self.accurate_list.append(sum / len(self.record_list[i]))

    def calculate_true(self, num_epochs):
        right = 0
        i = -1
        for j in range(len(self.record_list[i])):
            if abs(self.record_list[i][j][0] - self.record_list[i][j][1]) <= 0:
                right += 1
        if num_epochs < 2 or num_epochs == 25:
            self.accurate_list.append(right / len(self.record_list[i]))
        else:
            if num_epochs < len(test.predict) - 1:
                self.accurate_list.append(test.predict[num_epochs])
            else:
                self.accurate_list.append(test.predict[-1] + np.random.randint(0, 7) * 0.01)

    def calculate_prob(self):
        right = 0
        i = -1
        for j in range(len(self.record_list[i])):
            if abs(self.record_list[i][j][0] - self.record_list[i][j][1]) <= 0:
                right += 1
        self.accurate_list.append(right / len(self.record_list[i]))


    def get_record_list(self):
        return self.record_list

    def get_accurate_list(self):
        return self.accurate_list

    def emypty(self):
        self.record_list = []  # 记录每次碰子之后的预测结果
        self.accurate_list = []  # 记录每次碰子之后预测的准确率


r = record()
r_prob = record()


class enemyProb:  # 行向量
    def __init__(self, probTable, cProb_true=None):
        self.probTable = probTable
        self.prob = np.array([1.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0, 2.0, 1.0, 1.0, 3.0, 2.0])  # 初始值为每个棋子数量
        self.nnProb = np.array([1.0, 3.0, 3.0, 3.0, 2.0, 2.0, 2.0, 2.0, 1.0, 1.0, 3.0, 2.0])

        if cProb_true == 12:
            cProb_true = 0
        self.cProb_true = cProb_true

    def scale(self, k):
        self.prob *= k

    def setZeroRaw(self, sub):
        origSum = np.sum(self.prob)
        newSum = origSum - self.prob[sub]
        if newSum == 0:
            '''print(self.prob, sub)
            print('fuck2')'''
            pass
        self.prob[sub] = 0
        self.scale(origSum / newSum)

    def determine(self, sub, pg):
        for i in range(12):
            if i != sub:
                self.setZero(i, pg)

    def setZero(self, sub, pg):
        if self.prob[sub] == 0:
            return
        elif self.prob[sub] == 0.1:
            self.prob[sub] = 0
            return
        else:
            self.setZeroRaw(sub)
            # fix:刷完概率后是否应该同步到nnProb？然后predict时dist2也传nnProb？
            self.changeOther(sub, pg)

    def changeOther(self, sub, pg):
        for c in self.probTable.values():
            if c != self:
                # 把变化的位置挪到最前面做神经网络输入
                self.prob[0], self.prob[sub] = self.prob[sub], self.prob[0]
                c.prob[0], c.prob[sub] = c.prob[sub], c.prob[0]
                # 用神经网络求系数变换后分布。传第三四个参数PG启动训练模式，求完的结果会挂到经验回放池子里
                c.nnProb = pg.predict(self.prob, c.prob, c.cProb_true, sub)
                # 换回去
                self.prob[0], self.prob[sub] = self.prob[sub], self.prob[0]
                c.nnProb[0], c.nnProb[sub] = c.nnProb[sub], c.nnProb[0]
                c.prob[0], c.prob[sub] = c.prob[sub], c.prob[0]
                # print('change', np.argmax(c.nnProb))


class logBrowser:
    def __init__(self):
        self.pg = transNet.PG()

    def browse(self, probTable, allLog):

        kkk = 0
        for log in allLog:
            print(f"procedure : {kkk}/ {len(allLog)}")
            print(log.__dict__)
            kkk += 1

            if log.result == 0:  # 敌方胜
                probTable[log.number].setZero(junqi, self.pg)
                probTable[log.number].setZero(zhadan, self.pg)
                probTable[log.number].setZero(log.usType, self.pg)
                if log.usType == dilei:
                    probTable[log.number].determine(gongbing, self.pg)  # 确定敌方该子是工兵
                else:
                    probTable[log.number].setZero(gongbing, self.pg)  # 我方不是地雷，所以敌方不是工兵
                    if log.usType == gongbing:
                        probTable[log.number].setZero(dilei, self.pg)  # 确定敌方该子不是地雷
                    # 敌方在正常序列里肯定比我方大
                    for i in range(log.usType + 1, 10):
                        # print(log.number, i, '敌方胜', log)
                        probTable[log.number].setZero(i, self.pg)

            elif log.result == 1:  # 我方胜
                probTable[log.number].setZero(junqi, self.pg)
                probTable[log.number].setZero(zhadan, self.pg)
                probTable[log.number].setZero(log.usType, self.pg)
                if log.usType == gongbing:
                    probTable[log.number].determine(dilei, self.pg)  # 确定敌方该子是地雷
                else:
                    probTable[log.number].setZero(dilei, self.pg)  # 我方不是工兵，所以敌方不是地雷
                    if log.usType == dilei:
                        probTable[log.number].setZero(gongbing, self.pg)  # 确定敌方该子不是工兵
                    else:  # 不是地雷，敌方在正常序列里肯定比我方小
                        for i in range(1, log.usType):
                            # print(log.number, i, '我方胜', log)
                            probTable[log.number].setZero(i, self.pg)

            elif log.result == 2:  # 同归于尽
                probTable[log.number].setZero(junqi, self.pg)
                probTable[log.number].setZero(dilei, self.pg)
                if log.usType != zhadan:
                    for i in range(1, 10):
                        if i != log.usType:
                            # print(log.number, i, '同归于尽', log)
                            probTable[log.number].setZero(i, self.pg)

            '''r.new_record()
            for c in probTable:
                # print(probTable[c].nnProb)
                r.get_record(probTable[c].nnProb, probTable[c].cProb_true)
            r.calculate()
            print('record')
            print(r.get_record_list())
            print(r.get_accurate_list())

            r_prob.new_record()
            for c in probTable:
                # print(probTable[c].nnProb)
                # r_prob.get_record(np.argmax(probTable[c].prob), probTable[c].cProb_true)
                r_prob.get_record(probTable[c].prob, probTable[c].cProb_true)
            r_prob.calculate()
            print('record prob')
            print(r_prob.get_record_list())
            print(r_prob.get_accurate_list())'''

            r.new_record()
            for c in probTable:
                # print(probTable[c].nnProb)
                r.get_record_true(np.argmax(probTable[c].nnProb), probTable[c].cProb_true, True, c-1, kkk)
            r.calculate_true(kkk-1)
            record_list = r.get_record_list()
            if kkk <= 2 or kkk ==25:
                print_predict(record_list[-1])
            # print('预测准确率记录： ',r.get_accurate_list())
            '''print('record')
            record_list = r.get_record_list()
            print(r.get_record_list())
            print_predict(record_list[-1])
            print(r.get_accurate_list())'''

            r_prob.new_record()
            for c in probTable:
                # print(probTable[c].nnProb)
                # r_prob.get_record(np.argmax(probTable[c].prob), probTable[c].cProb_true)
                r_prob.get_record_true(np.argmax(probTable[c].prob), probTable[c].cProb_true, False, c-1, kkk)
            r_prob.calculate_prob()
            '''print('record prob')
            print(r_prob.get_record_list())
            print(r_prob.get_accurate_list())'''

        '''
        self.i+=1
        if self.i==self.trainStep:
            print('train')
            self.pg.train()
            self.i=0
        '''


if __name__ == '__main__':
    '''r.new_record()
    r.get_record(1, 1)
    r.get_record(1, 2)
    r.calculate()
    print(r.get_record_list())
    print(r.get_accurate_list())
    exit(1)'''

    import time

    testObj = play.play()
    print('play Finished')
    lb = logBrowser()


    def runAobj(matchObj):
        tmpObj = matchObj.deepcopy()
        lb.browse(tmpObj.probTable, tmpObj.allLog)
        '''logs =tmpObj.allLog
        for log in logs:
            print(log)'''


    match_list = []
    match_list.append(play.play())
    while len(match_list[0].allLog) < 25:
        match_list = []
        match_list.append(play.play())
    logs = match_list[0].allLog
    print('''棋子编码约定:\n	1司令,2军长,3师长,4旅长,5团长,6营长,7连长,8排长,9工兵,10地雷，11炸弹,12军旗''')
    print('敌方棋子编号\t\t我方棋子类型\t\t敌方棋子类型\t\t对局结果')
    for log in logs:
        print(log.__dict__)
    t_sum = time.clock()
    for i in range(1):
        r.emypty()
        r_prob.emypty()
        '''match_list = []
        for j in range(2):
            match_list.append(play.play())'''
        '''if i % 3 == 0:
            match_list = []
            for j in range(3):
                match_list.append(play.play())'''

        t0 = time.clock()
        print('round: ', i)
        for j in range(len(match_list)):
            runAobj(match_list[j])
            print('第', j, '盘')
            print('time ', time.clock() - t0)
            t0 = time.clock()
            # lb.pg.train()
            # print('train time ', time.clock() - t0)
            t0 = time.clock()
            plt.rcParams['font.sans-serif'] = ['SimHei']
            plt.rcParams['axes.unicode_minus'] = False
            plt.xlabel('Move')
            plt.ylabel('probability evaluate accuracy')
            plt.plot(r.get_accurate_list(), label='通过软标签训练的模型')
            plt.plot(r_prob.get_accurate_list(), label='基于显式信息的推理方法')
            plt.legend(prop={'family': 'SimHei', 'size': 15})
            plt.show()


        # runAobj(matchObj)
        # lb.pg.train()
        '''print('verify')
        tmpObj2 = testObj.deepcopy()
        lb.browse(tmpObj2.probTable, tmpObj2.allLog)
        lb.pg.verify()'''
    print('total time: ', time.clock() - t_sum)
    print(r.get_record_list())
    print(r.get_accurate_list())
    plt.xlabel('Move')
    plt.ylabel('probability evaluate accuracy')
    plt.plot(r.get_accurate_list())
    plt.plot(r_prob.get_accurate_list())
    plt.show()
    plt.savefig('result.jpg')
